package com.syqc.videos

import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioRecord
import android.media.MediaRecorder
import android.media.audiofx.AcousticEchoCanceler
import android.media.audiofx.AudioEffect
import android.media.audiofx.NoiseSuppressor
import com.syqc.comlib.utils.MLog
import com.syqc.monitor.R
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import java.io.IOException
import java.io.OutputStream
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
import java.util.concurrent.*
import kotlin.concurrent.thread

/** @author joker @date 2022/7/23$ 11:14$ */
class TalkHelper {
    //缓冲区字节大小
    private var bufferSize = 0

    //是否已关闭录音
    private var isClosed = false

    //是否正在录音
    private var isRecording = false

    //录音文件队列
    private var dataQueue: Queue<ShortArray>? = ConcurrentLinkedDeque()

    //声音队列
    private var voiceQueue: Queue<Array<Any>>? = ConcurrentLinkedQueue()

    //录音
    private var audioRecord: AudioRecord? = null

    //录音状态
    private var state = 0
    private val executorService: ExecutorService by lazy { Executors.newFixedThreadPool(10) }

    private val talkSocket = PublishSubject.create<Boolean>()
    fun talkObserver(): Observable<Boolean> = talkSocket.hide()

    private val talkImgSub = PublishSubject.create<Int>()
    fun talkImgObserver(): Observable<Int> = talkImgSub.hide()

    fun setSpeakerphoneOn(context: Context) {
        val audioManger = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        audioManger.mode = AudioManager.MODE_IN_COMMUNICATION
        audioManger.isSpeakerphoneOn = true
    }

    @SuppressLint("MissingPermission")
    private fun initAudioRecord() {
        if (audioRecord == null) {
            bufferSize =
                AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING)
            check(bufferSize > 0) { "没有最小buffer" }
            audioRecord = AudioRecord(
                AUDIO_INPUT,
                AUDIO_SAMPLE_RATE,
                AUDIO_CHANNEL,
                AUDIO_ENCODING,
                bufferSize
            )
            state = STATUS_READY
            val audioSessionId = audioRecord!!.audioSessionId
            initAEC(audioSessionId)
            initNS(audioSessionId)
        }
    }

    /** 开启录音 */
    private fun startRecord() {
        if (audioRecord!!.state == AudioRecord.STATE_INITIALIZED) {
            state = STATUS_START
            audioRecord!!.startRecording()
            recording()
        }
    }

    /** 开始录音 */
    private fun recording() {
        val buffer = ShortArray(bufferSize)
        state = STATUS_RECORDING
        isRecording = true
        while (isRecording) {
            val read = audioRecord?.read(buffer, 0, bufferSize)

            voiceQueue?.add(arrayOf(read!!, buffer))

            dataQueue?.add(buffer)
        }
    }

    /** 结束录音 */
    fun stopRecord() {
        if (audioRecord == null) return
        if (audioRecord!!.state == AudioRecord.STATE_INITIALIZED) {
            audioRecord!!.stop()
            state = STATUS_STOP
        }
        release()
    }

    /** 销毁录音 */
    fun destroy() {
        release()
        dataQueue!!.clear()
        dataQueue = null

        voiceQueue!!.clear()
        voiceQueue = null
        stopService()
    }

    /** 重置 */
    private fun release() {
        isRecording = false
        isClosed = true
        if (audioRecord != null) {
            audioRecord!!.release()
            audioRecord = null
        }
        thread {
            closeHttp()
        }
    }

    /**  */
    fun start(url: String) { 
        connectTimes = 0
        executorService.execute { initHttp(url) }
        executorService.execute { initDataQueue() }
        executorService.execute { initVoiceQueue() }
        executorService.execute {
            initAudioRecord()
            startRecord()
        }
    }

    /** 关闭ExecutorService */
    private fun stopService() {
        executorService.shutdown()
        try {
            if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
                executorService.shutdownNow()
            }
        } catch (e: InterruptedException) {
            executorService.shutdownNow()
        }
    }

    /** 初始化并开始处理数据 */
    private fun initDataQueue() {
        isClosed = false
        dataQueue!!.clear()
        while (!isClosed) {
            var data = dataQueue!!.poll()
            while (data != null && !isClosed) {
                val bytes = toByteArray(data)
                sendPcm(bytes)
                data = dataQueue!!.poll()
            }
            try {
                Thread.sleep(10)
            } catch (e: Exception) {
                e.fillInStackTrace()
            }
        }
    }

    /** Init voice queue */
    private fun initVoiceQueue() {
        isClosed = false
        voiceQueue!!.clear()
        while (!isClosed) {
            var data = voiceQueue!!.poll()
            while (data != null && !isClosed) {
                val size = data[0] as Int
                val buffer = data[1] as ShortArray
                var v = 0L
                for (tmp in buffer) {
                    v += tmp * tmp
                }
                talkImgSub.onNext(Math.log10(v / size.toDouble()).toInt())

                data = voiceQueue!!.poll()
            }
            try {
                Thread.sleep(100)
            } catch (e: Exception) {
                e.fillInStackTrace()
            }
        }

    }


    /** short[]转换为byte[] */
    private fun toByteArray(pcm: ShortArray): ByteArray {
        val size = pcm.size
        val dest = ByteArray(size shl 1)
        for (i in 0 until size) {
            dest[i * 2] = pcm[i].toByte()
            dest[i * 2 + 1] = (pcm[i].toInt() shr 8).toByte()
        }
        return dest
    }


    /**
     * ********************************************************************************************
     */
    private var connect: HttpURLConnection? = null
    private var os: OutputStream? = null
    private var link: String = ""
    private var isLinking = false

    //重连次数
    private var connectTimes = 0
    private fun initHttp(link: String) {
        this.link = link
        try {
            val url = URL(link)
            connect = url.openConnection() as HttpURLConnection
            connect!!.requestMethod = "POST"
            connect!!.connectTimeout = 10000
            connect!!.useCaches = false
            connect!!.doOutput = true
            connect!!.doInput = false
            connect!!.setRequestProperty("Transfer-Encoding", "chunked")
            connect!!.setChunkedStreamingMode(647)
            connect!!.connect()
            os = connect!!.outputStream
            isLinking = false
        } catch (e: Exception) {
            isLinking = false
            talkSocket.onNext(true)
        }

    }

    /**
     * 发送数据
     *
     * @param byteArray
     */
    private fun sendPcm(byteArray: ByteArray) {
        try { 
            os?.write(byteArray)
        } catch (e: Exception) {
            if (connectTimes >= 10) { 
                talkSocket.onNext(true)
                connectTimes = 0
                closeHttp()
            } else {
                if (!isLinking) {
                    connectTimes++
                    closeHttp()
                    executorService.execute {
                        isLinking = true
                        initHttp(link)
                    }
                }
            }
        }
    }

    /** 关闭链接 */
    private fun closeHttp() {
        try {
            os?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        isLinking = false
        connect?.disconnect()
        connect = null
    }


//    /**
//     */
//    private fun savePcm() {
//        try {
//            val file = File(getParentFile(BaseApp.application).absolutePath + "/audio.pcm")
//            if (!file.exists()) file.createNewFile()
//            val fos = FileOutputStream(file)
//            val bos = BufferedOutputStream(fos)
//            val dos = DataOutputStream(bos)
//            val buffer = ShortArray(bufferSize)
//            isRecording = true
//            while (isRecording) {
//                Log.e("录音====", "$bufferSize       $audioRecord     $dataQueue")
//                val read = audioRecord!!.read(buffer, 0, bufferSize)
//                for (i in 0 until read) {
//                    dos.writeShort(buffer[i].toInt())
//                }
//            }
//            stopRecord()
//            dos.close()
//            bos.close()
//            fos.close()
//        } catch (e: IOException) {
//            e.printStackTrace()
//        }
//    }
//
//    /**
//     */
//    private var audioTrack: AudioTrack? = null
//    private fun initTrack(bufferSize: Int) {
//        audioTrack = AudioTrack(
//            AudioManager.STREAM_MUSIC,
//            AUDIO_SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
//            AUDIO_ENCODING, bufferSize, AudioTrack.MODE_STREAM
//        )
//        if (audioTrack!!.state != AudioTrack.STATE_UNINITIALIZED) {
//            audioTrack!!.play()
//        }
//    }
//
//    fun play() {
//        println("开始播放声音================")
//        try {
//            val file = File(mContext.filesDir.path + "/audio.pcm")
//            val length = (file.length() / 2).toInt()
//            val shorts = ShortArray(length)
//            val fis = FileInputStream(file)
//            val bis = BufferedInputStream(fis)
//            val dis = DataInputStream(bis)
//            var i = 0
//            while (dis.available() > 0) {
//                shorts[i] = dis.readShort()
//                i++
//            }
//            fis.close()
//            bis.close()
//            dis.close()
//            initTrack(length)
//            audioTrack!!.write(shorts, 0, length)
//            audioTrack!!.stop()
//        } catch (e: IOException) {
//            e.printStackTrace()
//        }
//    }
//
//    private fun destroyTrack() {
//        if (audioTrack != null) {
//            audioTrack!!.stop()
//            audioTrack!!.release()
//            audioTrack = null
//        }
//    }


    /**  */
    companion object {
        //音频输入-麦克风
        //    private static final int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
        private const val AUDIO_INPUT = MediaRecorder.AudioSource.VOICE_COMMUNICATION

        //音频采样率
        private const val AUDIO_SAMPLE_RATE = 8000

        //声道 音频声道
        private const val AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO

        //编码 音频采样位
        private const val AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT

        /**  */ //录音状态
        private const val STATUS_READY = 0x10

        //录音开始
        private const val STATUS_START = 0x11

        //录音中
        private const val STATUS_RECORDING = 0x12

        //录音停止
        private const val STATUS_STOP = 0x13

        val voiceImg = intArrayOf(
            R.mipmap.ic_audio_0,
            R.mipmap.ic_audio_0,
            R.mipmap.ic_audio_0,
            R.mipmap.ic_audio_1,
            R.mipmap.ic_audio_2,
            R.mipmap.ic_audio_3,
            R.mipmap.ic_audio_4,
            R.mipmap.ic_audio_4,
            R.mipmap.ic_audio_4,
            R.mipmap.ic_audio_4
        )

    }
    //***********************************************************************************************/
    /** 判断回声消除是否可用 */
    private fun isAECAvailable(): Boolean {
        return AcousticEchoCanceler.isAvailable()
    }

    /** 判断噪音抑制是否可用 */
    private fun isNsAvailable(): Boolean {
        return NoiseSuppressor.isAvailable()
    }

    private fun initAEC(audioSession: Int) {
        if (isAECAvailable()) {
            val create = AcousticEchoCanceler.create(audioSession)
            val enabled = create?.setEnabled(true)
            if (AudioEffect.SUCCESS == enabled) {
                MLog.e("使用回声消除成功")
            }
        }
    }

    private fun initNS(audioSession: Int) {
        if (isNsAvailable()) {
            val create = NoiseSuppressor.create(audioSession)
            val enabled = create?.setEnabled(true)
            if (AudioEffect.SUCCESS == enabled) {
                MLog.e("使用噪音消除成功")
            }
        }
    }


}